using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GameComponents
{
    public enum Alignment { None = 0, Below, Min, OneThird, OneFourth, Center, TwoThirds, ThreeFourths, Max, Above }
    public enum FieldType { Button = 0, Image, Text }

    public class HUD
    {
        private SpriteBatch hudBatch;
        private Rectangle hudRect;

        public bool IsVisible = true;
        public int Border;
        private Dictionary<string, Window> dctWindows = new Dictionary<string, Window>();

        public HUD(SpriteBatch newDrawBatch, Rectangle newRect, int newBorder)
        { hudBatch = newDrawBatch; hudRect = newRect; Border = newBorder; }

        // ****** ACCESSORS ******

        public Rectangle Rect
        {
            get { return hudRect; }
            set
            {
                foreach (string windowName in dctWindows.Keys)
                {
                    dctWindows[windowName].WndRect = new Rectangle(dctWindows[windowName].WndRect.X + value.X - hudRect.X,
                        dctWindows[windowName].WndRect.Y + value.Y - hudRect.Y, dctWindows[windowName].WndRect.Width, dctWindows[windowName].WndRect.Height);
                    foreach (string fieldName in dctWindows[windowName].FieldNames)
                    {
                        dctWindows[windowName].ModifyField(fieldName, new Vector2(
                            dctWindows[windowName][fieldName].DrawRect.X + (value.X - hudRect.X),
                            dctWindows[windowName][fieldName].DrawRect.Y + (value.Y - hudRect.Y)));

                    }
                }
                hudRect = value;
            }
        }
        public Window this[string windowName]
        { 
            get
            {
                try
                { return dctWindows[windowName]; }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(windowName + " is not present.");
                    #endif
                    return new Window(new Rectangle(0, 0, 1, 1), 0);
                }
            }
        }

        // ****** USER METHODS ******

        public void CreateWindow(string newName, int newWidth, int newHeight, int newBorder, Alignment alignH, Alignment alignV)
        {
            Window windowPass = new Window(new Rectangle(0, 0, newWidth, newHeight), newBorder);
            windowPass.Align(alignH, alignV, hudRect, ref windowPass.WndRect, Border);
            // Add new window
            dctWindows.Add(newName, windowPass);
        }

        public void Draw()
        {
            if (IsVisible)
            {
                // Draw each window
                foreach (string windowName in dctWindows.Keys)
                { if (dctWindows[windowName].IsVisible) { dctWindows[windowName].Draw(hudBatch); } }
            }
        }

        public void Draw(string windowName)
        {
            if (IsVisible)
            {
                // Draw window
                dctWindows[windowName].Draw(hudBatch);
            }
        }

        // ****** PARTS ******

        public class Window
        {
            public bool IsVisible;
            public Rectangle WndRect;
            private Dictionary<string, Field> dctFields = new Dictionary<string, Field>();
            private int borderSize;
            private bool lineTrack;
            private Vector2Int currentLine;
            private Rectangle lastAdded;
            public int LineBorder;

            public Window(Rectangle newRect, int newBorder)
            {
                IsVisible = true;
                WndRect = newRect;
                borderSize = newBorder;
                lineTrack = false;
                currentLine = new Vector2Int(0, 0);
                lastAdded = new Rectangle(0, 0, 1, 1);
                LineBorder = 0;
            }

            // ****** ACCESSORS ******

            public int Border { get { return borderSize; } set { borderSize = value; } }
            public Field this[string fieldName]
            {
                get
                {
                    try
                    { return dctFields[fieldName]; }
                    catch
                    {
                        #if WINDOWS
                        System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                        #endif
                        return new CustomField(null, new Rectangle(0, 0, 1, 1), new Rectangle(0, 0, 1, 1), null, Color.White, null, null, Color.White, false);
                    }
                }
            }
            public List<string> FieldNames { get { return new List<string>(dctFields.Keys); } }
            public int FieldCount {get { return dctFields.Count; } }

            public bool EnableLineTrack
            {
                get { return lineTrack; }
                set
                {
                    lineTrack = value;
                    currentLine = new Vector2Int(WndRect.X + borderSize, WndRect.Y + borderSize);
                }
            }
            
            // ****** USER METHODS ******

            public void Align(Alignment newHAlign, Alignment newVAlign, Rectangle outerBounds, ref Rectangle intoBounds, int newBorder)
            {
                switch (newHAlign)
                {
                    case Alignment.None:
                        intoBounds.X = outerBounds.X;
                        break;
                    case Alignment.Below:
                        intoBounds.X = outerBounds.X;
                        break;
                    case Alignment.Min:
                        intoBounds.X = outerBounds.X + newBorder;
                        break;
                    case Alignment.OneFourth:
                        intoBounds.X = outerBounds.X + (int)(.25f * outerBounds.Width - .5f * intoBounds.Width);
                        break;
                    case Alignment.OneThird:
                        intoBounds.X = outerBounds.X + (int)(.33f * outerBounds.Width - .5f * intoBounds.Width);
                        break;
                    case Alignment.Center:
                        intoBounds.X = outerBounds.X + (int)((outerBounds.Width - intoBounds.Width) * .5f);
                        break;
                    case Alignment.TwoThirds:
                        intoBounds.X = outerBounds.X + (int)(.66f * outerBounds.Width - .5f * intoBounds.Width);
                        break;
                    case Alignment.ThreeFourths:
                        intoBounds.X = outerBounds.X + (int)(.75f * outerBounds.Width - .5f * intoBounds.Width);
                        break;
                    case Alignment.Max:
                        intoBounds.X = outerBounds.X + outerBounds.Width - intoBounds.Width - newBorder;
                        break;
                    case Alignment.Above:
                        intoBounds.X = outerBounds.X + outerBounds.Width - intoBounds.Width;
                        break;
                }
                switch (newVAlign)
                {
                    case Alignment.None:
                        intoBounds.Y = outerBounds.Y;
                        break;
                    case Alignment.Below:
                        intoBounds.Y = outerBounds.Y;
                        break;
                    case Alignment.Min:
                        intoBounds.Y = outerBounds.Y + newBorder;
                        break;
                    case Alignment.OneFourth:
                        intoBounds.Y = outerBounds.Y + (int)(.25f * outerBounds.Height - .5f * intoBounds.Height);
                        break;
                    case Alignment.OneThird:
                        intoBounds.Y = outerBounds.Y + (int)(.33f * outerBounds.Height - .5f * intoBounds.Height);
                        break;
                    case Alignment.Center:
                        intoBounds.Y = outerBounds.Y + (int)((outerBounds.Height - intoBounds.Height) * .5f);
                        break;
                    case Alignment.TwoThirds:
                        intoBounds.Y = outerBounds.Y + (int)(.66f * outerBounds.Height - .5f * intoBounds.Height);
                        break;
                    case Alignment.ThreeFourths:
                        intoBounds.Y = outerBounds.Y + (int)(.75f * outerBounds.Height - .5f * intoBounds.Height);
                        break;
                    case Alignment.Max:
                        intoBounds.Y = outerBounds.Y + outerBounds.Height - intoBounds.Height - newBorder;
                        break;
                    case Alignment.Above:
                        intoBounds.Y = outerBounds.Y + outerBounds.Height - intoBounds.Height;
                        break;
                }
            }

            public string Click(Rectangle targetRect)
            {
                string foundField = null;                
                foreach (string fieldName in dctFields.Keys)
                {
                    if (dctFields[fieldName].Interactive)
                    {
                        if (targetRect.Intersects(dctFields[fieldName].DrawRect))
                        { foundField = fieldName; break; }
                    }
                }
                return foundField;
            }

            public void CreateCustomField(string newName, Effect newEffect, Texture2D newImage, Color newImageColor, int newPadding, SpriteFont newFont,
                string newText, Color newTextColor, Alignment alignH, Alignment alignV, Alignment textAlignH, Alignment textAlignV, bool isInteractive)
            {
                Vector2 stringSize = newFont.MeasureString(newText);
                Rectangle rectPass = new Rectangle(currentLine.X, currentLine.Y, (int)stringSize.X + newPadding, (int)stringSize.Y + newPadding);
                if (lineTrack)
                { currentLine.X += rectPass.Width + LineBorder; }
                else
                { Align(alignH, alignV, WndRect, ref rectPass, borderSize); }

                // For tracking
                lastAdded = rectPass;

                // Set Title rect
                Vector2 titleSize = newFont.MeasureString(newText);
                Rectangle titlePass = new Rectangle(0, 0, (int)(titleSize.X), (int)(titleSize.Y));
                Align(textAlignH, textAlignV, rectPass, ref titlePass, borderSize);
                // Add Field
                dctFields.Add(newName, new CustomField(newEffect, rectPass, titlePass, newImage, newImageColor, newFont, newText, newTextColor, isInteractive));
            }

            public void CreateCustomField(string newName, Effect newEffect, Texture2D newImage, int newWidth, int newHeight, Color newImageColor, SpriteFont newFont,
                string newText, Color newTextColor, Alignment alignH, Alignment alignV, Alignment textAlignH, Alignment textAlignV, bool isInteractive)
            {
                Rectangle rectPass = new Rectangle(currentLine.X, currentLine.Y, newWidth, newHeight);
                if (lineTrack)
                { currentLine.X += rectPass.Width + LineBorder; }
                else
                { Align(alignH, alignV, WndRect, ref rectPass, borderSize); }

                // For tracking
                lastAdded = rectPass;

                // Set Title rect
                Vector2 titleSize = newFont.MeasureString(newText);
                Rectangle titlePass = new Rectangle(0, 0, (int)(titleSize.X), (int)(titleSize.Y));
                Align(textAlignH, textAlignV, rectPass, ref titlePass, borderSize);
                // Add Field
                dctFields.Add(newName, new CustomField(newEffect, rectPass, titlePass, newImage, newImageColor, newFont, newText, newTextColor, isInteractive));
            }

            public void CreateImageField(string newName, Effect newEffect, Texture2D newImage, int newWidth,
                int newHeight, Color newColor, Alignment alignH, Alignment alignV, bool isInteractive)
            {
                Rectangle rectPass = new Rectangle(currentLine.X, currentLine.Y, newWidth, newHeight);
                if (lineTrack)
                { currentLine.X += newWidth + LineBorder; }
                else
                { Align(alignH, alignV, WndRect, ref rectPass, borderSize); }
                // For tracking
                lastAdded = rectPass;

                dctFields.Add(newName, new ImageField(newEffect, rectPass, newImage, newColor, isInteractive));
            }

            public void CreateTextField(string newName, Effect newEffect, SpriteFont newFont, string newText,
                Color newColor, Alignment alignH, Alignment alignV, bool isInteractive)
            {
                Vector2 fontSize = newFont.MeasureString(newText);
                Rectangle rectPass = new Rectangle(currentLine.X, currentLine.Y, (int)fontSize.X, (int)fontSize.Y);
                
                if (lineTrack)
                { currentLine.X += (int)fontSize.X + LineBorder; }
                else
                { Align(alignH, alignV, WndRect, ref rectPass, borderSize); }
                lastAdded = rectPass;

                dctFields.Add(newName, new TextField(newEffect, rectPass, newFont, newText, newColor, isInteractive));
            }

            public void Draw(SpriteBatch drawBatch)
            {
                foreach (string fieldName in dctFields.Keys)
                {
                    try
                    {
                        Field fieldPass = dctFields[fieldName];
                        drawBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
                            RasterizerState.CullCounterClockwise, fieldPass.FieldEffect);
                        dctFields[fieldName].Draw(drawBatch);
                        drawBatch.End();
                    }
                    catch
                    {
                        #if WINDOWS
                        System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                        #endif
                    }
                }
            }

            // Modify Alignment
            public void ModifyField(string fieldName, Alignment alignH, Alignment alignV)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    Align(alignH, alignV, WndRect, ref fieldPass.DrawRect, borderSize);
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Color
            public void ModifyField(string fieldName, Color newColor)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    fieldPass.DrawColor = newColor;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Pre-Multiplied
            public void ModifyField(string fieldName, float newPreMultiplied)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    fieldPass.PreMultiplied = newPreMultiplied;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Dimensions
            public void ModifyField(string fieldName, int newWidth, int newHeight)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    fieldPass.DrawRect.Width = newWidth;
                    fieldPass.DrawRect.Height = newHeight;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Effect
            public void ModifyField(string fieldName, Effect newEffect)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    fieldPass.FieldEffect = newEffect;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Image
            public void ModifyField(string fieldName, Texture2D newImage)
            {
                try
                {
                    ImageField fieldPass = (ImageField)dctFields[fieldName];
                    fieldPass.Image = newImage;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Position
            public void ModifyField(string fieldName, Vector2 newPosition)
            {
                try
                {
                    Field fieldPass = dctFields[fieldName];
                    fieldPass.DrawRect.X = (int)newPosition.X;
                    fieldPass.DrawRect.Y = (int)newPosition.Y;
                    dctFields[fieldName] = fieldPass;
                }
                catch
                {
                    #if WINDOWS
                    System.Windows.Forms.MessageBox.Show(fieldName + " is not present.");
                    #endif
                }
            }

            // Modify Text
            public void ModifyField(string fieldName, string newText)
            {
                TextField fieldPass = (TextField)dctFields[fieldName];
                Vector2 newSize = fieldPass.Font.MeasureString(newText);
                fieldPass.DrawRect.Width = (int)newSize.X;
                fieldPass.DrawRect.Height = (int)newSize.Y;
                fieldPass.Text = newText;
                dctFields[fieldName] = fieldPass;
            }

            public void NewColumn()
            { currentLine.Y = WndRect.Y + borderSize; }

            public void ShiftColumn(int shiftColumnWidth)
            { currentLine.X += LineBorder + shiftColumnWidth; }

            public void NewLine()
            {
                currentLine.X = WndRect.X + borderSize;
                currentLine.Y += LineBorder + lastAdded.Height;
            }

            public void NewLine(int overrideLastHeight)
            {
                currentLine.X = WndRect.X + borderSize;
                currentLine.Y += LineBorder + overrideLastHeight;
            }

            public void RemoveField(string fieldName)
            { dctFields.Remove(fieldName); }
        }

        public abstract class Field
        {
            public bool IsVisible = true;
            public Effect FieldEffect;
            public Rectangle DrawRect;
            public Color DrawColor;
            public float PreMultiplied = 1;
            public bool Interactive;

            public Field(Effect newEffect, Rectangle newDrawRect, Color newColor, bool isInteractive)
            {
                DrawRect = newDrawRect;
                FieldEffect = newEffect;
                DrawColor = newColor;
                Interactive = isInteractive;
            }

            // ***** ACCESSORS *****

            public int X { get { return DrawRect.X; } set { DrawRect.X = value; } }
            public int Y { get { return DrawRect.Y; } set { DrawRect.Y = value; } }

            public int Width { get { return DrawRect.Width; } set { DrawRect.Width = value; } }
            public int Height { get { return DrawRect.Height; } set { DrawRect.Height = value; } }

            public int EndX { get { return DrawRect.X + DrawRect.Width; } }
            public int EndY { get { return DrawRect.Y + DrawRect.Height; } }

            // ***** USER METHODS *****

            public abstract void Draw(SpriteBatch drawBatch);
        }

        public class CustomField : Field
        {
            public Rectangle TitleRect;
            public Texture2D Image;
            public SpriteFont Font;
            public string Text;
            public Color TextColor;

            public CustomField(Effect newEffect, Rectangle newDrawRect, Rectangle newTitleRect, Texture2D newImage, Color newImageColor,
                SpriteFont newFont, string newText, Color newTextColor, bool isInteractive)
                : base(newEffect, newDrawRect, newImageColor, isInteractive)
            {
                TitleRect = newTitleRect;
                Image = newImage;
                Font = newFont;
                Text = newText;
                TextColor = newTextColor;
            }

            public override void Draw(SpriteBatch drawBatch)
            {
                if (IsVisible)
                {
                    drawBatch.Draw(Image, DrawRect, DrawColor * PreMultiplied);
                    drawBatch.DrawString(Font, Text, new Vector2(TitleRect.X, TitleRect.Y), TextColor);
                }
            }
        }

        public class ImageField : Field
        {
            public Texture2D Image;

            public ImageField(Effect newEffect, Rectangle newDrawRect, Texture2D newImage, Color newColor, bool isInteractive)
                : base(newEffect, newDrawRect, newColor,isInteractive)
            { Image = newImage; }

            public override void Draw(SpriteBatch drawBatch)
            {
                if (IsVisible)
                { drawBatch.Draw(Image, DrawRect, DrawColor * PreMultiplied); }
            }
        }

        public class TextField : Field
        {
            public SpriteFont Font;
            public string Text;

            public TextField(Effect newEffect, Rectangle newDrawRect, SpriteFont newFont, string newText, Color newColor, bool isInteractive)
                : base(newEffect, newDrawRect, newColor, isInteractive)
            { Font = newFont; Text = newText; }

            public override void Draw(SpriteBatch drawBatch)
            {
                if (IsVisible)
                { drawBatch.DrawString(Font, Text, new Vector2(DrawRect.X, DrawRect.Y), DrawColor * PreMultiplied); }
            }
        }

        public struct Vector2Int
        {
            public int X;
            public int Y;

            public Vector2Int(int newX, int newY)
            { X = newX; Y = newY; }
        }
    } // End HUD
}
